﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using TetrisLibrary.extras;
using System.Drawing.Drawing2D;

namespace TetrisLibrary {

    public delegate void OnPreviewNextBlock(int[] blockShape);

    public delegate void OnGameOver();

    public partial class TetrisView : UserControl {

        public event OnPreviewNextBlock OnPreviewNextBlockEvent;
        public event OnGameOver OnGameOverEvent;

        public static int MapColumnCount = 12;
        public static int MapRowCount = 21;
        public static int BlockSize = 30;
        public static int[][][] TetrisShapes = new int[][][]
            { //所有方块图形和状态
                   new int[][]{//I
                            new int[]{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
                            new int[]{0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}
                    },
                    new int[][]{//S
                            new int[]{0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}
                    },
                    new int[][]{//Z
                            new int[]{1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}
                    },
                    new int[][]{//J
                            new int[]{0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
                    },
                    new int[][]{//O
                            new int[]{1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
                    },
                    new int[][]{//L
                            new int[]{1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
                    },
                    new int[][]{//T
                            new int[]{0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
                            new int[]{1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                            new int[]{0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}
                    }
            };

        private MusicController musicController = MusicController.Instance();
        private Random random = new Random();
        private Timer tetrisTimer = new Timer();
        public int[][] TetrisMap = new int[MapColumnCount][];
        private int nextBlockShape = -1, nextTurnState = -1;
        private int blockShape = -1, turnState = -1;
        private int curX = 3, curY = 0;
        private bool isGameRunning = false;
        private bool isGameOver = false;

        private Pen pen = new Pen(Color.White);

        public TetrisView() {
            InitializeComponent();
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            NewTetrisMap();
            DrawLimitWall();
        }

        private void tetrisGameView_Paint(object sender, PaintEventArgs e) {
            Graphics graphics = e.Graphics;
            graphics.TranslateTransform(-BlockSize, 0, MatrixOrder.Prepend);
            //绘制游戏背景
            graphics.Clear(Color.White);
            pen.Color = Color.Gray;
            for (int y = 0; y < MapRowCount; y++) {
                for (int x = 0; x < MapColumnCount; x++) {
                    graphics.FillRectangle(new SolidBrush(Color.DarkGray), x + 1 + BlockSize * x, y + 1 + BlockSize * y, BlockSize, BlockSize);
                }
            }

            if (blockShape != -1 && turnState != -1) {
                //绘制当前的方块状态
                for (int i = 0; i < 16; i++) {
                    if (TetrisShapes[blockShape][turnState][i] == 1) {
                        int dx = i % 4 + 1 + curX;
                        int dy = i / 4 + curY;
                        graphics.FillRectangle(new SolidBrush(Color.Red), dx + 1 + dx * BlockSize,
                            dy + 1 + dy * BlockSize, BlockSize, BlockSize);
                    }
                }
            }

            //绘制已经存在的方块
            for (int y = 0; y < MapRowCount; y++) {
                for (int x = 0; x < MapColumnCount; x++) {
                    if (TetrisMap[x][y] == 1) {
                        int dx = x + 1 + x * BlockSize;
                        int dy = y + 1 + y * BlockSize;
                        graphics.FillRectangle(new SolidBrush(Color.Green), dx, dy, BlockSize, BlockSize);
                    }
                    if (TetrisMap[x][y] == 2) {
                        int dx = x + 1 + x * BlockSize;
                        int dy = y + 1 + y * BlockSize;
                        graphics.FillRectangle(new SolidBrush(Color.White), dx, dy, BlockSize, BlockSize);
                    }
                }
            }
            Width = (MapColumnCount - 2) + 1 + BlockSize * (MapColumnCount - 2);
            Height = (MapRowCount - 1) + 1 + BlockSize * (MapRowCount - 1);
        }

        public bool IsGameRunning => isGameRunning;

        public bool IsGameFinished => isGameOver;

        public void StartGame() {
            NewTetrisBlock();
            tetrisTimer.Interval = 1000;
            tetrisTimer.Tick += (sender, args) => {
                MoveTo(Direction.Down);
            };
            tetrisTimer.Start();
            isGameRunning = true;
            isGameOver = false;
            musicController.Play();
        }

        public void GoPlayGame() {
            tetrisTimer.Start();
            musicController.Play();
        }

        public void PauseGame() {
            tetrisTimer.Stop();
            isGameRunning = false;
            isGameOver = false;
            musicController.Stop();
        }

        public void StopGame() {
            tetrisTimer.Stop();
            isGameRunning = false;
            isGameOver = true;
            nextBlockShape = nextTurnState = -1;
            blockShape = turnState = -1;
            NewTetrisMap();
            DrawLimitWall();
            musicController.Stop();
        }

        public void NewTetrisMap() {
            for (int i = 0; i < MapColumnCount; i++) {
                TetrisMap[i] = new int[MapRowCount];
                for (int j = 0; j < TetrisMap[i].Length; j++) {
                    TetrisMap[i][j] = 0;
                }
            }
        }

        public void ClearTetrisMap() {
            for (int i = 0; i < MapColumnCount; i++) {
                for (int j = 0; j < TetrisMap[i].Length; j++) {
                    TetrisMap[i][j] = 0;
                }
            }
            DrawLimitWall();
        }

        public void DrawLimitWall() {
            for (int i = 0; i < MapColumnCount; i++) {
                TetrisMap[i][20] = 2;
            }
            for (int i = 0; i < MapRowCount; i++) {
                TetrisMap[0][i] = 2;
                TetrisMap[11][i] = 2;
            }
        }

        public void NewTetrisBlock() {
            if (nextBlockShape == -1 && nextTurnState == -1) {
                blockShape = random.Next(0, 6);
                turnState = random.Next(0, 3);
                GenericNextTetrisBlock();
            } else {
                blockShape = nextBlockShape;
                turnState = nextTurnState;
                GenericNextTetrisBlock();
            }
            OnPreviewNextBlockEvent?.Invoke(TetrisShapes[nextBlockShape][nextTurnState]);
            curX = 3;
            curY = 0;
            if (IsGameOver(curX, curY)) { //游戏结束
                tetrisTimer.Stop(); //停止计时器
                OnGameOverEvent?.Invoke();
                isGameRunning = false;
                isGameOver = true;
            }
        }

        private void GenericNextTetrisBlock() {
            nextBlockShape = random.Next(0, 6);
            nextTurnState = random.Next(0, 3);
        }

        public bool CheckBlockNormal(int x, int y, int blockShape, int turnState) {
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if ((TetrisShapes[blockShape][turnState][i * 4 + j] == 1 && TetrisMap[x + j + 1][y + i] == 1)
                        || (TetrisShapes[blockShape][turnState][i * 4 + j] == 1 && TetrisMap[x + j + 1][y + i] == 2)) {
                        return false;
                    }
                }
            }
            return true;
        }

        public bool IsGameOver(int x, int y) {
            return !CheckBlockNormal(x, y, blockShape, turnState);
        }

        public void MakeShapeTurn() {
            int tempTurn = turnState;
            turnState = (turnState + 1) % 4;
            if (!CheckBlockNormal(curX, curY, blockShape, turnState)) {
                turnState = tempTurn;
            }
            Invalidate();
        }

        public void MoveTo(Direction direction) {
            switch (direction) {
                case Direction.Left:
                    if (CheckBlockNormal(curX - 1, curY, blockShape, turnState)) {
                        curX -= 1;
                    }
                    break;
                case Direction.Up:
                    break;
                case Direction.Right:
                    if (CheckBlockNormal(curX + 1, curY, blockShape, turnState)) {
                        curX += 1;
                    }
                    break;
                case Direction.Down:
                    if (blockShape != -1 && turnState != -1 && CheckBlockNormal(curX, curY + 1, blockShape, turnState)) {
                        curY += 1;
                    } else {
                        AddToTetrisMap();
                        NewTetrisBlock();
                        DeleteTetrisLine();
                    }
                    break;
            }
            Invalidate();
        }

        private void AddToTetrisMap() {
            int j = 0;
            for (int a = 0; a < 4; a++) {
                for (int b = 0; b < 4; b++) {
                    if (TetrisShapes[blockShape][turnState][j] == 1) {
                        TetrisMap[curX + b + 1][curY + a] = TetrisShapes[blockShape][turnState][j];
                    }
                    j++;
                }
            }
        }

        private void DeleteTetrisLine() {
            int c = 0;
            for (int b = 0; b < 21; b++) {
                for (int a = 0; a < 12; a++) {
                    if (TetrisMap[a][b] == 1) {
                        c += 1;
                        if (c == 10) {
                            //该行满
                            for (int d = b; d > 0; d--) {
                                for (int e = 0; e < 12; e++) {
                                    //上方方块下移
                                    TetrisMap[e][d] = TetrisMap[e][d - 1];
                                }
                            }
                        }
                    }
                }
                c = 0;
            }
        }

    }
}
